package tp.pr2;

public class RobotEngine {
	
	/**
	 * 
	 * @param initialPlace sitio desde donde se parte
	 * @param direction	dirección inicial
	 * @param cityMap mapa de la ciudad
	 */
	public RobotEngine (City cityMap, Place initialPlace, Direction direction) {
		this.position = initialPlace;
		this.direction = direction;
		this.city = cityMap;
		this.fuel = FUEL_INITIAL;
		this.points = POINTS_INITIAL;
		this.pocket = new ItemContainer();
	}
	
	/**
	 *
	 * @param message
	 */
	private void robotSays(String message) {
		System.out.println(ROBOT_NAME + OUTPUT_SAYS + message);
	}
	/**
	 * Prints both the fuel level and recycled material amount
	 */
	public void sayFuelAndRM () { //pública porque la necesitan el fuel y el garbage al usarse
		System.out.println(OUTPUT_SAYFUEL + this.fuel + Interpreter.LINE_SEPARATOR + OUTPUT_SAYRM + this.points);
	}
	
	
	
	/**
	 * Adds an amount of fuel to the robot (it can be negative)
	 * @param fuel Amount of fuel added to the robot
	 */
	public void addFuel(int fuel) {
		this.fuel += fuel;
		if (this.fuel < 0) 
			this.fuel = 0;
	}
	/**
	 * Adds an amount of fuel to the robot (it can be negative)
	 * @param weight Amount of fuel added to the robot
	 */
	public void addRecycledMaterial (int weight) {
		points += weight;
	}
		/**
	 * for testing
	 * @return current fuel level
	 */
	public int getFuel() {
		return this.fuel;
	}	
	/**
	 * for testing
	 * @return current engine points
	 */
	public int getRecycledMaterial() {
		return this.points;
	}
	
	/**
	 * Returns the street opposite the robot
	 * @return The street which the robot is facing, or null, if there is not any street in this direction
	 */
	public Street getHeadingStreet() {
		return city.lookForStreet(this.position, this.direction);
	}
	
	/**
	 * Mueve al robot en caso de que se pueda hacia la siguiente dirección
	 */
	private void move() {
		//recorremos todo el bucle que terminamos al encontrar una dirección válida (que por construcción será única)
		Street currentStreet = this.getHeadingStreet();
		if (currentStreet == null) {
			robotSays(OUTPUT_STREET_NO.replace("<dir>", this.direction.toString()));
			return; //no hay calle, no te mueves, fin
		}
		
		if (!currentStreet.isOpen()) {
			robotSays(OUTPUT_STREET_CLOSED);
			return;
		}
		
		this.position = currentStreet.nextPlace(this.position);
		robotSays(this.direction.moveDirection());
		System.out.println(this.position.toString() + Interpreter.LINE_SEPARATOR);
		this.sayAndUpdateDataAfterFuelConsumingAction(FUEL_DECREMENT_MOVE);
	}
	/**
	 * rotates the robot
	 * @param rotation Where to rotate the robot
	 */
	private void turn (Rotation rotation) {
		//no se tiene en cuenta el caso unknown porque la instrucción es válida 
		if (rotation == Rotation.LEFT)
			this.direction = this.direction.leftTurn();
		else
			this.direction = this.direction.rightTurn();

		this.sayAndUpdateDataAfterFuelConsumingAction(FUEL_DECREMENT_TURN);
	}
	/**
	 * prints all info related to the robot status after a fuel consumption
	 * @param consumedFuel fuel that was consumed
	 */
	private void sayAndUpdateDataAfterFuelConsumingAction(int consumedFuel) {
		this.addFuel(consumedFuel);
		this.sayFuelAndRM();
		System.out.println(this.direction.sayDirection());
	}
	/**
	 * tries to pick an item from the place where the robot is
	 * @param id id of the item to be picked up
	 */
	private void pick (String id) {
		Item itemInHand = this.position.pickItem(id);
		if (itemInHand == null) {
			this.robotSays(OUTPUT_ITEM_NOITEM + id);
			return;
		}
		if (this.pocket.addItem(itemInHand)) //TODO esto va sin setID, pero si no, no valida
			this.robotSays(OUTPUT_ITEM_PICKED + itemInHand.getId());
		else {
			this.robotSays(OUTPUT_ITEM_NOPICKED + itemInHand.getId());
			this.position.addItem(itemInHand);
		}
	}
	/**
	 * scans an item from the pocket
	 * @param id
	 */
	private void scan (String id) {
		if (id.equals(""))  { //Caso de escanear todo
			String inventory = this.pocket.toString();
			if (inventory.equals("")) this.robotSays(OUTPUT_SCAN_NONE);
			else {	
				this.robotSays(OUTPUT_SCAN_ALL);
				System.out.println(inventory + Interpreter.LINE_SEPARATOR); //TODO sobra line separator, está para el validador
			}
		}
		else { //Caso de escanear uno solo
			if (this.pocket.getItem(id) == null)
				this.robotSays(OUTPUT_SCAN_FAIL);
			else
				this.robotSays(this.pocket.getItem(id).toString());
		}
	}
	/**
	 * Uses an item from the pocket
	 * @param id of the item to be used
	 */
	private void operate(String id) {
		Item temp = this.pocket.getItem(id);
		if (temp == null)
			this.robotSays(OUTPUT_OPERATE_NOITEM + id);
		else {
			//de ser usado, el objeto dirá qué ha hecho. Si no, saltará el fallo y lo mostrará el robot.
			if (!temp.use(this, this.position))
				this.robotSays(OUTPUT_OPERATE_FAIL + id);
			
			//si ya no puede ser usado lo quitamos
			if (!temp.canBeUsed()) {
				this.pocket.pickItem(id);
				this.robotSays(OUTPUT_OPERATE_USED.replace("<id>", id));
				return; 
			}
		}
	}
	
	
	/**
	 * Crea el interprete de instrucciones y el escaner para leer los comandos
	 */
	public void startEngine (){
		//inicializamos el interpreter, el escáner, y mostramos los datos de lugar de incio del robot
		boolean salir = false;
		java.util.Scanner sc = new java.util.Scanner(System.in);
		System.out.println(this.position.toString() + Interpreter.LINE_SEPARATOR);
		this.sayFuelAndRM();
		System.out.println(this.direction.sayDirection());

		while (!salir) {
			//System.out.println("");
			System.out.print(PROMPT);
			Instruction instruction = Interpreter.generateInstruction(sc.nextLine());
			//TESTING System.out.println("---Orden: " + instruction.toString() + "---");
			
			if (!instruction.isValid()) {
				robotSays(OUTPUT_WRONGCOMMAND);
			} else {
				//switch de los diferentes tipos de comandos
				switch (instruction.getAction()) {
				case MOVE: this.move(); break;
				case TURN: this.turn(instruction.getRotation()); break;
				case HELP: System.out.println(Interpreter.interpreterHelp()); break;
				case QUIT: 
					salir = true; 
					robotSays(OUTPUT_QUIT);
					break;
				case PICK: this.pick(instruction.getId()); break;
				case SCAN: this.scan(instruction.getId()); break;				
				case OPERATE: this.operate(instruction.getId()); break;
				default: break;
				}
			}
			
			if (this.position.isSpaceship()){
				robotSays(OUTPUT_ATSPACESHIP);
				salir = true;
			}
			if (this.fuel == 0) {
				robotSays(OUTPUT_FUEL_NOFUEL);
				salir = true;
			}
			
		}
		
		sc.close();
	}
	


	//constantes de mensajes a mostrar
	public static final String ROBOT_NAME = "WALL·E";
	private static final String PROMPT = ROBOT_NAME + " > ";
	private static final String OUTPUT_SAYS = " says: ";
	private static final String OUTPUT_QUIT = "I have communication problems. Bye bye";
	private static final String OUTPUT_WRONGCOMMAND = "I do not understand. Please repeat";
	private static final String OUTPUT_ATSPACESHIP = "I am at my space ship. Bye Bye";
	private static final String OUTPUT_STREET_NO = "There is no street in direction <dir>";
	private static final String OUTPUT_STREET_CLOSED = "Arrggg, there is a street but it is closed!";
	private static final String OUTPUT_ITEM_PICKED = "I am happy! Now I have  "; //TODO hay dos espacios pero solo debería haber uno (para el validador)
	private static final String OUTPUT_ITEM_NOPICKED = "I am stupid! I had already the object ";
	private static final String OUTPUT_ITEM_NOITEM = "Ooops, this place has not the object ";
	private static final String OUTPUT_SCAN_ALL = "I am carrying the following items";
	private static final String OUTPUT_SCAN_NONE = "My inventory is empty";
	private static final String OUTPUT_SCAN_FAIL = "I lost it when I was looking for garbage";
	private static final String OUTPUT_OPERATE_USED = "What a pity! I have no more <id> in my inventory";
	private static final String OUTPUT_OPERATE_FAIL = "I have problems using the object ";
	private static final String OUTPUT_OPERATE_NOITEM = "I don't have that";
	private static final String OUTPUT_FUEL_NOFUEL = "I run out of fuel. I cannot move. Shutting down...";
	private static final String OUTPUT_SAYFUEL = "   * My power is ";
	private static final String OUTPUT_SAYRM = "   * My recycled material is: ";
	
	//constantes numéricas
	private static final int FUEL_INITIAL = 50;
	private static final int FUEL_DECREMENT_TURN = -1;
	private static final int FUEL_DECREMENT_MOVE = -5;
	
	//private static final int FUEL_MAX = 9001;
	private static final int POINTS_INITIAL = 0;
	
	
	private Place position;
	private Direction direction;
	private City city;
	private int fuel;
	private int points;
	private ItemContainer pocket;
	
}
